#!/usr/bin/env -S deno run --allow-read=. --allow-write=. --allow-run=git
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

/** This copies the test files according to the config file `cli/tests/node_compat/config.jsonc` */

import { walk } from "../../test_util/std/fs/walk.ts";
import { sep } from "../../test_util/std/path/mod.ts";
import { ensureFile } from "../../test_util/std/fs/ensure_file.ts";
import { writeAll } from "../../test_util/std/streams/write_all.ts";
import { withoutAll } from "../../test_util/std/collections/without_all.ts";
import { relative } from "../../test_util/std/path/posix.ts";

import { config, ignoreList } from "../../cli/tests/node_compat/common.ts";

const encoder = new TextEncoder();

const NODE_VERSION = config.nodeVersion;

const NODE_IGNORED_TEST_DIRS = [
  "addons",
  "async-hooks",
  "cctest",
  "common",
  "doctool",
  "embedding",
  "fixtures",
  "fuzzers",
  "js-native-api",
  "node-api",
  "overlapped-checker",
  "report",
  "testpy",
  "tick-processor",
  "tools",
  "v8-updates",
  "wasi",
  "wpt",
];

const VENDORED_NODE_TEST = new URL("node/test/", import.meta.url);
const NODE_COMPAT_TEST_DEST_URL = new URL(
  "../../cli/tests/node_compat/test/",
  import.meta.url,
);

async function getNodeTests(): Promise<string[]> {
  const paths: string[] = [];
  const rootPath = VENDORED_NODE_TEST.href.slice(7);
  for await (
    const item of walk(VENDORED_NODE_TEST, { exts: [".js"] })
  ) {
    const path = relative(rootPath, item.path);
    if (NODE_IGNORED_TEST_DIRS.every((dir) => !path.startsWith(dir))) {
      paths.push(path);
    }
  }

  return paths.sort();
}

function getDenoTests() {
  return Object.entries(config.tests)
    .filter(([testDir]) => !NODE_IGNORED_TEST_DIRS.includes(testDir))
    .flatMap(([testDir, tests]) => tests.map((test) => testDir + "/" + test));
}

async function updateToDo() {
  using file = await Deno.open(new URL("./TODO.md", import.meta.url), {
    write: true,
    create: true,
    truncate: true,
  });

  const missingTests = withoutAll(await getNodeTests(), await getDenoTests());

  await file.write(encoder.encode(`<!-- deno-fmt-ignore-file -->
# Remaining Node Tests

NOTE: This file should not be manually edited. Please edit \`cli/tests/node_compat/config.json\` and run \`deno task setup\` in \`tools/node_compat\` dir instead.

Total: ${missingTests.length}

`));
  for (const test of missingTests) {
    await file.write(
      encoder.encode(
        `- [${test}](https://github.com/nodejs/node/tree/v${NODE_VERSION}/test/${test})\n`,
      ),
    );
  }
}

async function clearTests() {
  console.log("Cleaning up previous tests");
  for await (
    const file of walk(NODE_COMPAT_TEST_DEST_URL, {
      includeDirs: false,
      skip: ignoreList,
    })
  ) {
    await Deno.remove(file.path);
  }
}

/** Checks if file has entry in config.json */
function hasEntry(file: string, suite: string) {
  return Array.isArray(config.tests[suite]) &&
    config.tests[suite].includes(file);
}

async function copyTests() {
  console.log("Copying test files...");

  for await (const entry of walk(VENDORED_NODE_TEST, { skip: ignoreList })) {
    const fragments = entry.path.split(sep);
    // suite is the directory name after test/. For example, if the file is
    // "node_compat/node/test/fixtures/policy/main.mjs"
    // then suite is "fixtures/policy"
    const suite = fragments.slice(fragments.indexOf("node_compat") + 3, -1)
      .join("/");
    if (!hasEntry(entry.name, suite)) {
      continue;
    }

    const dest = new URL(`${suite}/${entry.name}`, NODE_COMPAT_TEST_DEST_URL);
    await ensureFile(dest);
    const destFile = await Deno.open(dest, {
      create: true,
      truncate: true,
      write: true,
    });
    const srcFile = await Deno.open(
      new URL(`${suite}/${entry.name}`, VENDORED_NODE_TEST),
    );
    // Add header to js files
    if (dest.pathname.endsWith("js")) {
      await writeAll(
        destFile,
        encoder.encode(`// deno-fmt-ignore-file
// deno-lint-ignore-file

// Copyright Joyent and Node contributors. All rights reserved. MIT license.
// Taken from Node ${NODE_VERSION}
// This file is automatically generated by \`tools/node_compat/setup.ts\`. Do not modify this file manually.

`),
      );
    }
    await srcFile.readable.pipeTo(destFile.writable);
  }
}

// main

await clearTests();
await copyTests();
await updateToDo();

if (Deno.args[0] === "--check") {
  const cmd = new Deno.Command("git", { args: ["status", "-s"] });
  const { stdout } = await cmd.output();

  if (stdout.length > 0) {
    console.log("The following files have been changed:");
    console.log(new TextDecoder().decode(stdout));
    Deno.exit(1);
  }
}
